/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *  SRVBlackfinController.m - iPhoneSRV console for Surveyor SRV-1 and SVS 
 *
 *  Created by Nick Kitchener.  Copyright (C) 2009  Surveyor Corporation 
 *            and Nick Kitchener.  All rights reserved.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details (www.gnu.org/licenses)
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#import "SRVBlackfinController.h"
#import "SRVCancelAllAndCloseOperation.h"

@implementation SRVBlackfinController
@synthesize inputStream, outputStream, targetURL, portNumber;

-(id)init {
	if( self=[super init] ) {
		operationQueue = [[[NSOperationQueue alloc]init] retain];
		[operationQueue setMaxConcurrentOperationCount:1]; // serial queue, vitally important.
		queueLock = [[NSObject alloc] init];
		queueOpen = YES;
	}
	return self;
}

+(SRVBlackfinController*)controllerNeedingConfiguration {
	return [[SRVBlackfinController alloc]init];
}

+(SRVBlackfinController*)controllerWithInitialValues:(NSString*)targetURL targetPort:(NSInteger)port {
	SRVBlackfinController *ret=[SRVBlackfinController controllerNeedingConfiguration];
	ret.targetURL=targetURL;
	ret.portNumber=port;
	return ret;
}

-(void)dealloc {
	queueLock = nil;
	operationQueue=nil;
	if(inputStream!=nil)
		[self closeConnection:nil];
	[super dealloc];
}

-(void)queueOperation:(SRVNSOperation*)operation {
	@synchronized(queueLock) {
		if( queueOpen ) {
			[operation setBlackfinController:self];
			if( [operation class]==[SRVCancelAllAndCloseOperation class] ) {
				queueOpen=NO;
				[operationQueue cancelAllOperations];
			}
			[operationQueue addOperation:operation];
		}
	}
}

// Now comes the funky bit.. listen up carefully.
// 1. A thread exists that has called this function - it's mostly likely the UI/application thread
// 2. A random thread owned by the system will be used to execute each NSOperation that exists in the serial queue.
//
// 3. The Stream socket pair is created on the UI/app thread that calls "openConnection"
// 4. Each SRVNSOperation, in turn, uses set stream client for the input and output streams and schedules
//    them on the NSOperation thread runloop to read/write the actual data.
// 5. At the end of each SRVNSOperation unschedules and tears up the clients, leaving the steam socket pair alone.
//

-(void)openConnection:(id)obj {

	NSLog(@"Attempting to open streams to target %@, port =%i", targetURL, portNumber);
	
	CFStreamCreatePairWithSocketToHost( kCFAllocatorDefault,
									   (CFStringRef)targetURL,
									   portNumber,
									   &inputStream,
									   &outputStream);
		
	if( !CFReadStreamOpen(inputStream) ) {
		CFStreamError streamError = CFReadStreamGetError(inputStream);
		if( streamError.error != 0 ) {
			if( streamError.domain == kCFStreamErrorDomainPOSIX ) {
				NSLog(@"%i: error opening read stream: %s", portNumber, strerror(streamError.error));
			} else if( streamError.domain == kCFStreamErrorDomainMacOSStatus ) {
				NSLog(@"%i: error opening read stream: %i", portNumber, streamError.error);
			}
		}
	}
	
	if( !CFWriteStreamOpen(outputStream) ) {
		CFStreamError streamError = CFWriteStreamGetError(outputStream);
		if( streamError.error != 0 ) {
			if( streamError.domain == kCFStreamErrorDomainPOSIX ) {
				NSLog(@"%i: error opening write stream: %s", portNumber, strerror(streamError.error));
			} else if( streamError.domain == kCFStreamErrorDomainMacOSStatus ) {
				NSLog(@"%i: error opening write stream: %i", portNumber, streamError.error);
			}
		}
	}
}

-(void)closeConnection:(id)obj {
	NSLog(@"%i: controller closing connection", portNumber);
	if(inputStream!=nil) {
		CFReadStreamClose(inputStream);
		CFRelease(inputStream);
		inputStream = nil;
	}
	
	if(outputStream!=nil) {
		CFWriteStreamClose(outputStream);
		CFRelease(outputStream);
		outputStream = nil;
	}
}

-(void)controllerOperationCompleted:(id)operation {
}

-(void)openQueue {
	@synchronized(queueLock) {
		queueOpen=YES;
	}
}

@end

